using System;
using System.Collections.Generic;
using Tools.Domain.Specifications;
using Web.App.Infrastructure.Extensions;
using Web.App.Infrastructure.Security.Contracts;
using Webshop20.Domain.Common;
using Webshop20.Domain.Common.Exceptions;
using Webshop20.Domain.Customers.DomainMessages;
using Webshop20.Domain.Shared.Payment.Core;
using Webshop20.Helper;
using Webshop20.Model.Customers.Specifications;
using Tools.Domain.Specifications.Constraints;

namespace Webshop20.Domain.Customers.Core
{
    [Serializable]
    public class Customer : Entity, IEMailAdressUnderSpecificationValidation
    {

        private const string PATTERN_FOR_REGISTRATION_KEY = "{0}*{1}*{2}";
        public virtual DateTime RegistrationDate { get; private set; }
        public virtual string FirstName { get; private set; }
        public virtual string LastName { get; private set; }
        public virtual Adress Adress { get; private set; }
        public virtual string EMail { get; private set; }
        public virtual CustomerState State { get; set; }
        public virtual IPayment Payment { get; set; }

        public Customer(
            string firstName,
            string lastName,
            string eMail,
            IPayment payment, 
            DateTime registrationDate,
            Adress adress 
            )
            
        {
            FirstName = firstName;
            LastName = lastName;
            Adress = adress;
            EMail = eMail;
            Payment = payment;
            State = CustomerState.pending;
            RegistrationDate = registrationDate;
        }



        public Customer(){}


        public virtual void ChangeStateTo(CustomerState newState)
        {
            State = newState;
        }

        public virtual void RegisteredAt(DateTime date)
        {
            if(date == null)
                throw new ArgumentNullException("date");

            RegistrationDate = date;
        }

        public virtual void SetCustomerData(
            string firstName,
            string lastName,
            string eMail,
            string street,
            string postalCode,
            string city,
            string country,
            string payment,
            object [] paymentData,
            IConstraintSet<Customer> customerConstraintSet
            )
        {
            Type t = Type.GetType("Webshop20.Domain.Shared.Payment.Core." + payment+ ",Webshop20.Domain.Shared.Payment");
            Payment = (IPayment)Activator.CreateInstance(t, paymentData);
            SetCustomerData(firstName, lastName, eMail, street, postalCode, city, country, customerConstraintSet);
        }

        public virtual void SetCustomerData(
            string firstName, 
            string lastName, 
            string eMail, 
            string street, 
            string postalCode, 
            string city,
            string country, 
            IConstraintSet<Customer> customerConstraintSet
            )
        {


            var setCustomerDataDTO = new SetCustomerDataData(firstName, lastName, eMail, Id);
            if (!customerConstraintSet
                     .ForMethod(x => x.SetCustomerData(null, null, null, null, null, null, null, null))
                     .PreconditionsAreSatisfiedBy(
                     setCustomerDataDTO
                     ))
            {
                ThrowNewDomainException(customerConstraintSet);
                return;

            }

            FirstName = firstName;
            LastName = lastName;
            EMail = eMail;
            IEnumerable<ISpecificationError> adressSpecificationErrors;
            Adress = Adress.Create(street, city, postalCode, country, out adressSpecificationErrors);



            if (!customerConstraintSet.ForMethod(x => x.SetCustomerData( null, null, null, null, null, null, null,null)).PostconditionsAreSatisfiedBy(this))
            {
                ThrowNewDomainException(customerConstraintSet);
                return;
            }
        }

        private void ThrowNewDomainException(IConstraintSet<Customer> customerConstraintSet)
        {
            throw new DomainException(
                
                Mappings.MapSpecificationErrorsToRuleFailures(  
                    customerConstraintSet.ForMethod(x => x.SetCustomerData(null, null, null, null, null, null, null, null))
                        .GetPreconditionSpecificationErrors<SetCustomerDataData>(),
                    "CUSTOMER", 
                    "SetCustomerData", 
                    "CUSTOMER_DATA_INVALID"
                    )
                );
        }
        
        public virtual bool IsInPendigState()
        {
            return !Adress.Exists() && State == CustomerState.pending;
        }

        public virtual void ResetNamesAndEMail()
        {
            FirstName = LastName = EMail = null;
        }

        public virtual string GenerateRegistrationKey(IEncryptionService hashService)
        {
            if(hashService == null)
                throw new ArgumentNullException();

            string registrationKey = String.Format(PATTERN_FOR_REGISTRATION_KEY, FirstName, LastName, EMail);

            return hashService.EncryptString(registrationKey);
        }




    }


}